package crp.services.api.graphql.loaders;

import com.google.common.collect.Maps;
import com.qwlabs.lang.C2;
import crp.core.workflow.app.WorkflowService;
import crp.core.workflow.domain.models.WorkflowTaskStatus;
import crp.services.api.graphql.models.WorkflowModelPayload;
import crp.services.api.graphql.models.WorkflowNodePayload;
import org.apache.commons.compress.utils.Sets;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Source;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

@GraphQLApi
@RequestScoped
public class WorkflowModelPayloadLoader {
    private final WorkflowService workflowService;

    @Inject
    public WorkflowModelPayloadLoader(WorkflowService workflowService) {
        this.workflowService = workflowService;
    }

    public List<@NotNull List<@NotNull WorkflowNodePayload>> nodes(@Source List<WorkflowModelPayload> models) {
        Set<String> instanceIds = C2.set(models, WorkflowModelPayload::getInstanceId, Objects::nonNull);
        Map<String, Set<String>> instanceBeenNodeIds = Maps.newHashMap();
        Map<String, String> instanceCurrentNodeIds = Maps.newHashMap();
        var tasks = workflowService.taskSearcher().byInstanceIds(instanceIds, null);
        tasks.forEach(task -> {
            String nodeId = task.getContext().getNodeId();
            instanceBeenNodeIds.computeIfAbsent(task.getInstanceId(), instanceId -> Sets.newHashSet())
                    .add(nodeId);
            if (task.getStatus() == WorkflowTaskStatus.PENDING) {
                instanceCurrentNodeIds.putIfAbsent(task.getInstanceId(), nodeId);
            }
        });
        return C2.list(models, model -> C2.list(model.getDefinition().getNodes(), node -> {
            Set<String> beenNodeIds = instanceBeenNodeIds.get(model.getInstanceId());
            String currentNodeId = instanceCurrentNodeIds.get(model.getInstanceId());
            return WorkflowNodePayload.of(node,
                    Objects.equals(currentNodeId, node.getId()),
                    beenNodeIds.contains(node.getId()));
        }));
    }
}
